﻿namespace LarsW.MexEdmxFixer
{
    using System;
    using System.IO;
    using System.Xml;

    /// <summary>
    /// This tools fix the broken EDMX output from the mex.exe tool (part of SQL Modeling tools - November 09 CTP)
    /// MSDN Forum thread: http://social.msdn.microsoft.com/Forums/en-US/oslo/thread/37ffe91b-de57-4804-8e3d-38100d155a0c
    /// </summary>
    class Program
    {
        const string CsNs = "http://schemas.microsoft.com/ado/2008/09/mapping/cs";
        const string EdmxNs = "http://schemas.microsoft.com/ado/2008/10/edmx";

        static void Main(string[] args)
        {
            if (args.Length != 1)
            {
                Console.WriteLine("syntax: MexEdmxFixer.exe file.edmx");
                Environment.Exit(-1);
            }

            XmlDocument doc = new XmlDocument();

            // Use XmlReader/StringReader to mitigate UTF-16 problems.
            var reader =XmlTextReader.Create(new StringReader(File.ReadAllText(args[0])));

            doc.Load(reader);
            XmlNamespaceManager mgr = new XmlNamespaceManager(doc.NameTable);
            mgr.AddNamespace("edmx", EdmxNs);
            mgr.AddNamespace("cs", CsNs);

            var nodes = doc.SelectNodes("/edmx:Edmx/edmx:Runtime/edmx:Mappings/cs:Mapping[@Space = 'C-S']/cs:EntityContainerMapping/cs:EntitySetMapping", mgr);
            foreach (XmlElement element in nodes)
            {
                var storeEntitySetAttribute = element.Attributes["StoreEntitySet"];
                element.Attributes.Remove(storeEntitySetAttribute);
                var typeNameAttribute = element.Attributes["TypeName"];
                element.Attributes.Remove(typeNameAttribute);

                var scalarPropertyElements = element.SelectNodes("cs:ScalarProperty", mgr);
                var entityTypeMappingElement = doc.CreateElement("EntityTypeMapping", CsNs);
                entityTypeMappingElement.Attributes.Append(typeNameAttribute);
                var mappingFragmentElement = doc.CreateElement("MappingFragment", CsNs);
                mappingFragmentElement.Attributes.Append(storeEntitySetAttribute);
                entityTypeMappingElement.AppendChild(mappingFragmentElement);
                element.AppendChild(entityTypeMappingElement);
                
                foreach (XmlElement scalarPropertyElement in scalarPropertyElements)
                {
                    element.RemoveChild(scalarPropertyElement);
                    mappingFragmentElement.AppendChild(scalarPropertyElement);
                }
            }

            // Insert the follow elements as sub elements to the <edmx:Edmx><edmx:Designer /> element.
            // 
            //<Connection>
            //    <DesignerInfoPropertySet>
            //        <DesignerProperty Name="MetadataArtifactProcessing" Value="EmbedInOutputAssembly" />
            //    </DesignerInfoPropertySet>
            //</Connection>
            //<Options>
            //    <DesignerInfoPropertySet>
            //        <DesignerProperty Name="ValidateOnBuild" Value="true" />
            //        <DesignerProperty Name="EnablePluralization" Value="True" />
            //        <DesignerProperty Name="IncludeForeignKeysInModel" Value="True" />
            //    </DesignerInfoPropertySet>
            //</Options>

            var designerElement = doc.SelectSingleNode("/edmx:Edmx/edmx:Designer", mgr) as XmlElement;
            var connectionElement = doc.CreateElement("Connection", EdmxNs);
            var connectionDesignerInfoPropertySetElement = doc.CreateElement("DesignerInfoPropertySet", EdmxNs);
            var metadataArtifactProcessingDesignerPropertyElement = doc.CreateElement("DesignerProperty", EdmxNs);
            var metadataArtifactProcessingDesignerNameAttribute = doc.CreateAttribute("Name");
            metadataArtifactProcessingDesignerNameAttribute.Value = "MetadataArtifactProcessing";
            var metadataArtifactProcessingDesignerValueAttribute = doc.CreateAttribute("Value");
            metadataArtifactProcessingDesignerValueAttribute.Value = "EmbedInOutputAssembly";
            metadataArtifactProcessingDesignerPropertyElement.Attributes.Append(metadataArtifactProcessingDesignerNameAttribute);
            metadataArtifactProcessingDesignerPropertyElement.Attributes.Append(metadataArtifactProcessingDesignerValueAttribute);
            
            connectionDesignerInfoPropertySetElement.AppendChild(metadataArtifactProcessingDesignerPropertyElement);
            connectionElement.AppendChild(connectionDesignerInfoPropertySetElement);

            
            var optionsElement = doc.CreateElement("Options", EdmxNs);
            var optionsDesignerInfoPropertySetElement = doc.CreateElement("DesignerInfoPropertySet", EdmxNs);

            var validateOnBuildDesignerPropertyElement = doc.CreateElement("DesignerProperty", EdmxNs);
            var validateOnBuildDesignerPropertyNameAttribute = doc.CreateAttribute("Name");
            validateOnBuildDesignerPropertyNameAttribute.Value = "ValidateOnBuild";
            var validateOnBuildDesignerPropertyValueAttribute = doc.CreateAttribute("Value");
            validateOnBuildDesignerPropertyValueAttribute.Value = "True";
            validateOnBuildDesignerPropertyElement.Attributes.Append(validateOnBuildDesignerPropertyNameAttribute);
            validateOnBuildDesignerPropertyElement.Attributes.Append(validateOnBuildDesignerPropertyValueAttribute);
            optionsDesignerInfoPropertySetElement.AppendChild(validateOnBuildDesignerPropertyElement);

            var enablePluralizationDesignerPropertyElement = doc.CreateElement("DesignerProperty", EdmxNs);
            var enablePluralizationDesignerPropertyNameAttribute = doc.CreateAttribute("Name");
            enablePluralizationDesignerPropertyNameAttribute.Value = "Name";
            var enablePluralizationDesignerPropertyValueAttribute = doc.CreateAttribute("Value");
            enablePluralizationDesignerPropertyValueAttribute.Value = "True";
            enablePluralizationDesignerPropertyElement.Attributes.Append(enablePluralizationDesignerPropertyNameAttribute);
            enablePluralizationDesignerPropertyElement.Attributes.Append(enablePluralizationDesignerPropertyValueAttribute);
            optionsDesignerInfoPropertySetElement.AppendChild(enablePluralizationDesignerPropertyElement);

            var includeForeignKeysInModelDesignerPropertyElement = doc.CreateElement("DesignerProperty", EdmxNs);
            var includeForeignKeysInModelDesignerPropertyNameAttribute = doc.CreateAttribute("Name");
            includeForeignKeysInModelDesignerPropertyNameAttribute.Value = "IncludeForeignKeysInModel";
            var includeForeignKeysInModelDesignerPropertyValueAttribute = doc.CreateAttribute("Value");
            includeForeignKeysInModelDesignerPropertyValueAttribute.Value = "True";
            includeForeignKeysInModelDesignerPropertyElement.Attributes.Append(includeForeignKeysInModelDesignerPropertyNameAttribute);
            includeForeignKeysInModelDesignerPropertyElement.Attributes.Append(includeForeignKeysInModelDesignerPropertyValueAttribute);
            optionsDesignerInfoPropertySetElement.AppendChild(includeForeignKeysInModelDesignerPropertyElement);

            var diagramsElement = doc.SelectSingleNode("edmx:Edmx/edmx:Designer/edmx:Diagrams", mgr);

            optionsElement.AppendChild(optionsDesignerInfoPropertySetElement);
            designerElement.InsertBefore(connectionElement, diagramsElement); 
            designerElement.InsertBefore(optionsElement, diagramsElement);
            

            doc.Save(args[0]);
        }
    }
}
